////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.  All rights reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Gadgeteer.Interfaces
{
    using System;
    using Microsoft.SPOT;
    using Microsoft.SPOT.Hardware;
    using Gadgeteer.Modules;

    internal class NativeDigitalOutput : Socket.SocketInterfaces.DigitalOutput
    {
        private OutputPort _port;

        public NativeDigitalOutput(Socket socket, Socket.Pin pin, bool initialState, Module module, Cpu.Pin cpuPin)
        {
            if (cpuPin == Cpu.Pin.GPIO_NONE)
            {
                // this is a mainboard error but should not happen since we check for this, but it doesnt hurt to double-check
                throw Socket.InvalidSocketException.FunctionalityException(socket, "DigitalOutput");
            }

            _port = new OutputPort(cpuPin, initialState);
        }

        public override void Write(bool state)
        {
            _port.Write(state);
        }

        public override bool Read()
        {
            return _port.Read();
        }

        public override void Dispose()
        {
            _port.Dispose();
        }
    }


    /// <summary>
    /// Represents digital output on a single pin.
    /// </summary>
    public class DigitalOutput
    {
        internal readonly Socket.SocketInterfaces.DigitalOutput Interface;

        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socket">The socket for the digital output interface.</param>
        /// <param name="pin">The pin used by the digital output interface.</param>
        /// <param name="initialState">The initial state to place on the digital output interface port.</param>
        /// <param name="module">The module using this interface (which can be null if unspecified).</param>
        public DigitalOutput(Socket socket, Socket.Pin pin, bool initialState, Module module)
        {
            Cpu.Pin reservedPin = socket.ReservePin(pin, module);

            // native implementation is preferred to an indirected one
            if (reservedPin == Cpu.Pin.GPIO_NONE && socket.DigitalOutputIndirector != null)
                Interface = socket.DigitalOutputIndirector(socket, pin, initialState, module);

            else
                Interface = new NativeDigitalOutput(socket, pin, initialState, module, reservedPin);
        }

        /// <summary>
        /// Writes a value to the interface port output. 
        /// </summary>
        /// <param name="state">The value to be written to the port output.</param>
        public void Write(bool state)
        {
            Interface.Write(state);
        }

        /// <summary>
        /// Reads a Boolean value at the interface port input. 
        /// </summary>
        /// <returns>The current value of the port (either 0 or 1).</returns>
        public bool Read()
        {
            return Interface.Read();
        }

        /// <summary>
        /// Returns the <see cref="Socket.SocketInterfaces.DigitalOutput" /> for a <see cref="DigitalOutput" /> interface.
        /// </summary>
        /// <param name="this">An instance of <see cref="DigitalOutput" />.</param>
        /// <returns>The <see cref="Socket.SocketInterfaces.DigitalOutput" /> for <paramref name="this"/>.</returns>
        public static explicit operator Socket.SocketInterfaces.DigitalOutput(DigitalOutput @this)
        {
            return @this.Interface;
        }

        /// <summary>
        /// Releases all resources used by the interface.
        /// </summary>
        public void Dispose()
        {
            Interface.Dispose();
        }
    }
}
